﻿/*
 * @Author: liuhandong
 * @Date: 2020-9-21 09:27:39
 */
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.ARFoundation;
using UnityEngine.XR.ARSubsystems;

public class ARManager : MonoBehaviour
{
    public enum AvaliableState
    {
        None,
        NotAvaliable,
        Avaliable
    }
    public enum State
    {
        Closed,
        Opening,
        Paused,
        Open
    }
    public static ARManager Instance = null;
    public event System.Action onRunningStateChanges;
    public AvaliableState avaliableState => mAvaliableState;
    public State runningState => mRunState;
    private AvaliableState mAvaliableState = AvaliableState.None;
    private State mRunState = State.Closed;
    private ARSession mSession = null;
    private GameObject mSessionObj = null;
    private ARScene mMainSpace = null;
    private Camera mPreviousMainCamera;
    private Camera mARCamera;

    public IEnumerator Initialize()
    {
        if(mAvaliableState == AvaliableState.None)
        {
            if ((ARSession.state == ARSessionState.None) ||
                (ARSession.state == ARSessionState.CheckingAvailability))
            {
                yield return ARSession.CheckAvailability();
            }

            if (ARSession.state == ARSessionState.Unsupported)
            {
                mAvaliableState =  AvaliableState.NotAvaliable;
            }
            else
            {
                mAvaliableState = AvaliableState.Avaliable;
            }
        }
    }

    public bool OpenAR(GameObject sessionPrefab, GameObject originPrefab)
    {
        if(mSessionObj == null)         //not in ar session
        {
            mPreviousMainCamera = Camera.main;
            if(mPreviousMainCamera != null)
            {
                mPreviousMainCamera.gameObject.SetActive(false);
            }
            mSessionObj = GameObject.Instantiate(sessionPrefab);
            mMainSpace = new ARScene(this, originPrefab);
            mSession = mSessionObj.GetComponent<ARSession>();
            mRunState = State.Opening;
            _RefreshStatus(); 
            Debug.Log("ARManager.OpenAR ----> opened");
            onRunningStateChanges?.Invoke();
            return true;
        }
        else
        {
            return false;
        }
    }

    public void CloseAR()
    {
        if(mSessionObj != null)         //in ar session
        {
            Debug.Log("ARManager.OpenAR ----> closed");
            mMainSpace.Dispose();
            GameObject.Destroy(mSessionObj);
            mMainSpace = null;
            mSessionObj = null;
            if(mPreviousMainCamera != null)
            {
                mPreviousMainCamera.gameObject.SetActive(true);
            }
            mPreviousMainCamera = null;
            mRunState = State.Closed;
            onRunningStateChanges?.Invoke();
        }
    }

    public ARScene GetMainScene()
    {
        return mMainSpace;
    }

    public bool IsSupportARKitFaceBlend()
    {
#if UNITY_IOS
        var faceDescriptors = new List<XRFaceSubsystemDescriptor>();
        SubsystemManager.GetSubsystemDescriptors(faceDescriptors);
        return faceDescriptors.Count > 0;
#else
        return false;
#endif
    }

    private void Awake() {
        Instance = this;
        ARSession.stateChanged += _OnARStateChange;
    }

    private void OnDestroy() {
        Instance = null;
        ARSession.stateChanged -= _OnARStateChange;   
    }
    private void _OnARStateChange(ARSessionStateChangedEventArgs arg)
    {
        _RefreshStatus();
    }

    private void _RefreshStatus()
    {
        var sessionState = ARSession.state;
        if(sessionState == ARSessionState.Ready || sessionState == ARSessionState.SessionTracking)
        {
            mAvaliableState = AvaliableState.Avaliable;
        }
        else if(sessionState == ARSessionState.Unsupported)
        {
            mAvaliableState = AvaliableState.NotAvaliable;
        }

        if(sessionState == ARSessionState.SessionTracking)
        {
            mRunState = State.Open;
            onRunningStateChanges?.Invoke();
        }
        else if(sessionState == ARSessionState.Unsupported)
        {
            mRunState = State.Closed;
            onRunningStateChanges?.Invoke();
        }
    }
}
